/*
 * Copyright (c) JiaChaoYang 2023-7-27 MongoPlus版权所有
 * 适度编码益脑，沉迷编码伤身，合理安排时间，享受快乐生活。
 * email: j15030047216@163.com
 * phone: 15030047216
 * weChat: JiaChaoYang_
 */

package com.mongo.plus.demo.service.impl;

import java.lang.reflect.UndeclaredThrowableException;
import java.util.concurrent.atomic.AtomicInteger;

import com.anwen.mongo.service.impl.ServiceImpl;
import com.mongo.plus.demo.entity.User;
import com.mongo.plus.demo.repository.UserRepository;
import com.mongo.plus.demo.service.UserService;

import com.mongodb.MongoCommandException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;


/**
 * @Description:
 * @BelongsProject: mongo-plus-demo
 * @Version: 1.0
 */

@Slf4j
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    TransactionTemplate mongoTransactionTemplate;

    @Autowired
    UserRepository userRepository;

    @Override
    public void test() {

    }

    @Override
    public void test2() {

    }

    AtomicInteger atomicInteger = new AtomicInteger(0);

    @Retryable(value = {OptimisticLockingFailureException.class, MongoCommandException.class}, maxAttempts = 5, backoff = @Backoff(delay = 100))
    @Override
    public Boolean updateUserWithRetryById(User user) throws OptimisticLockingFailureException, MongoCommandException {
        log.info("atomicInteger:[{}]", atomicInteger.incrementAndGet());

        try {
            return mongoTransactionTemplate.execute(status -> {
                User dbUser = userRepository.getById(user.getId());
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                dbUser.setUserName(user.getUserName());
                return userRepository.updateById(dbUser);
            });
        } catch (Exception e) {
            Throwable rootCause = getRootCause(e);
            if (rootCause instanceof com.mongodb.MongoCommandException) {
                // 处理 MongoDB 异常逻辑
                throw (MongoCommandException) rootCause;
            }
            throw e;
        }
    }

    @Recover
    public Boolean updateUserExceptionRecover(MongoCommandException mongoCommandException) {
        log.error("My mongoCommand", mongoCommandException);
        throw mongoCommandException;
     }

    @Recover
    public Boolean updateUserExceptionRecover(OptimisticLockingFailureException optimisticLockingFailureException) {
        log.error("My OptimisticLock", optimisticLockingFailureException);
        throw optimisticLockingFailureException;
    }

    /**
     * 获取根异常信息
     * 当Spring AOP 或其他基于 JDK 动态代理的框架中当被代理的方法抛出了受检异常（checked exception)
     * 但接口方法没有声明 throws，Spring 会将其封装为 UndeclaredThrowableException，只有通过这种方法才能获取原始异常
     *
     * @param throwable
     * @return
     */
    public static Throwable getRootCause(Throwable throwable) {
        while (throwable.getCause() != null && throwable.getCause() != throwable) {
            throwable = throwable.getCause();
        }
        return throwable;
    }
}